package device

import (
	"database/sql"
	"errors"
	"fmt"
	"net"
	"reflect"
	"strings"

	"github.com/alecthomas/log4go"
	"pac/client"
	"pac/conf"
	database "pac/db"
	"pac/location"
	pb "pac/proto"
)

type loginReq struct {
	reqPkt

	Id []byte
}

type loginRsp struct {
	rspPkt
}

func init() {
	cmdPktMap[CmdLoginReq.String()] = reflect.TypeOf((*loginReq)(nil))
	cmdPktMap[CmdLoginRsp.String()] = reflect.TypeOf((*loginRsp)(nil))
}

func (p *loginReq) String() string {
	return fmt.Sprintf("%s: ID: %X", p.CmdStr(), p.Id)
}

func (p *loginReq) parseParam() error {
	if len(p.Payload) == 0 {
		return errors.New("loginReq.parseParam: invalid param")
	}

	p.Id = p.Payload

	return nil
}

func (p *loginRsp) parseParam() error {
	return nil
}

func (p *loginReq) adjustCmd() {
	if p != nil {
		p.Cmd = CmdLoginReq
	}
}

func (p *loginRsp) adjustCmd() {
	if p != nil {
		p.Cmd = CmdLoginRsp
	}
}

func (p *loginReq) marshalParams() ([]byte, error) {
	param, err := p.reqPkt.marshalParams()
	if err != nil {
		return param, err
	}
	return append(param, p.Id...), nil
}

func (p *loginRsp) marshalParams() ([]byte, error) {
	return p.rspPkt.marshalParams()
}

// Marshal 编码
func (p *loginReq) Marshal() ([]byte, error) {
	return Marshal(p)
}

// Marshal 编码
func (p *loginRsp) Marshal() ([]byte, error) {
	return Marshal(p)
}

// Process 处理
func (p *loginReq) Process(cli *Client) error {
	processLogin(cli, p)
	return nil
}

// Process 处理
func (p *loginRsp) Process(cli *Client) error {
	return nil
}

func processLogin(cli *Client, req *loginReq) {
	rsp := &loginRsp{}
	rsp.fromReq(req)

	rsp.ErrCode = doLogin(cli, req.Id)

	err := cli.SendPacket(rsp)
	if err != nil {
		log4go.Warn("SendPacket failure: %v", err)
	}

	if rsp.ErrCode == ErrSuccess {
		client.NotifToUser(&pb.OnDeviceOnlineStatusReq{
			DeviceId: cli.GetID(),
			Online:   true,
		})
	}
}

func doLogin(cli *Client, id []byte) ErrCode {
	if len(id) == 0 {
		log4go.Info("login: id is empty")
		return ErrInvalidParam
	}

	deviceID := fmt.Sprintf("%X", id)

	if conf.DBAvailable {

		var deviceIDInDB, addressInDB sql.NullString

		db := database.GetDb()
		err := db.QueryRow("SELECT device_id,address FROM device WHERE device_id=?", deviceID).Scan(&deviceIDInDB, &addressInDB)
		if err != nil {
			if err != sql.ErrNoRows {
				log4go.Error(err)
				return ErrFailure
			}
		}
		var address string
		if len(addressInDB.String) == 0 {
			host, _, err := net.SplitHostPort(cli.Conn.RemoteAddr().String())
			if err == nil {
				errCode, l := location.LbIP(host)
				if errCode == location.ErrSuccess {
					address = l.Addr.Province
					if address != l.Addr.City {
						address += l.Addr.City
					}
				}
			}
		}
		if len(deviceIDInDB.String) == 0 {
			_, err = db.Exec("INSERT INTO device(device_id,address) VALUES(?,?)", deviceID, address)
			if err != nil {
				if !strings.Contains(err.Error(), "Duplicate entry") {
					log4go.Error(err)
					return ErrFailure
				}
			}
		} else if len(addressInDB.String) == 0 {
			_, err = db.Exec("UPDATE device SET address=? WHERE device_id=?", address, deviceID)
			if err != nil {
				log4go.Error(err)
				return ErrFailure
			}
		}
	}

	cli.DeviceID = deviceID

	client.PutClient(cli)

	return ErrSuccess
}
